package com.tengju.motivation.domain.model.activity;

import com.tengju.motivation.domain.shared.DomainException;
import com.tengju.motivation.domain.shared.DomainExceptionCode;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 活动目标组聚合根
 */
public class ActivityTargetSet {

    private final ActivityId activityId;
    private final Set<ActivityTarget> targets;
    private final Map<Long, Map<LocalDate, ActivityTarget>> indexMap = new HashMap<>();

    public ActivityTargetSet(ActivityId activityId, Collection<ActivityTarget> targets) {
        this.activityId = activityId;
        this.targets = new HashSet<>(targets);
        if (!CollectionUtils.isEmpty(targets)) {
            targets.forEach(target -> {
                if (!this.activityId.sameValueAs(target.getActivityId())) {
                    throw DomainException.error(DomainExceptionCode.INVALID_DATA, target.getActivityId());
                }
                Map<LocalDate, ActivityTarget> dateMap = indexMap.get(target.getTargetSubjectId());
                if (dateMap == null) {
                    dateMap = new HashMap<>();
                }
                dateMap.put(target.getActivityTime().toLocalDate(), target);
                indexMap.put(target.getTargetSubjectId(), dateMap);
            });
        }
    }


    public void add(ActivityTarget target) {
        if (!activityId.sameValueAs(target.getActivityId())) {
            throw DomainException.error(DomainExceptionCode.INVALID_DATA, target.getActivityId());
        }
        targets.add(target);
        Map<LocalDate, ActivityTarget> dateMap = indexMap.get(target.getTargetSubjectId());
        if (dateMap == null) {
            dateMap = new HashMap<>();
        }
        dateMap.put(target.getActivityTime().toLocalDate(), target);
        indexMap.put(target.getTargetSubjectId(), dateMap);
    }

    public boolean contains(ActivityId activityId, Long targetSubjectId, LocalDate date) {
        return this.activityId.sameValueAs(activityId) &&
                Optional.ofNullable(indexMap.get(targetSubjectId))
                        .map(map -> map.get(date) != null)
                        .orElse(false);
    }

    public void remove(ActivityTarget target) {
        if (this.activityId.sameValueAs(target.getActivityId())) {
            ActivityTarget removeTarget = Optional.ofNullable(indexMap.get(target.getTargetSubjectId()))
                    .map(map -> map.remove(target.getActivityTime().toLocalDate()))
                    .orElse(null);
            targets.remove(removeTarget);
        }
    }

    public Set<ActivityTarget> getTargets() {
        return targets;
    }

    public boolean isEmpty() {
        return CollectionUtils.isEmpty(targets);
    }

    public List<TargetId> getIds() {
        return Optional.ofNullable(targets)
                .map(list -> list.stream()
                        .map(ActivityTarget::getTargetId)
                        .collect(Collectors.toList())
                )
                .orElse(null);
    }

}
